package com.hk.core.web.filter;

import com.hk.commons.util.ArrayUtils;
import com.hk.commons.util.CollectionUtils;
import com.hk.commons.util.JsonUtils;
import com.hk.commons.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.filter.CommonsRequestLoggingFilter;

import java.util.*;
import java.util.function.Predicate;


/**
 * http 请求 信息日志输出，包括耗时
 *
 * @author Administrator
 */
public class HttpCommonsRequestLoggingFilter extends CommonsRequestLoggingFilter {

    private final Set<String> ignoreSuffixUris = ArrayUtils.asHashSet(".js", ".css", ".html");

    private static final String REQUEST_START_TIME_ATTR = "XW_COMMONS_START_REQUEST_TIME";

    public HttpCommonsRequestLoggingFilter(Collection<String> headers) {
        setIncludePayload(true);
        setIncludeClientInfo(false);
        setIncludeHeaders(true);
        setIncludeQueryString(true);
        setMaxPayloadLength(1_000);
        setBeforeMessagePrefix("");
        setBeforeMessageSuffix("");
        setHeaderPredicate(headerName -> headers.stream().anyMatch(item -> StringUtils.equalsIgnoreCase(item, headerName)));
    }

    public void putAll(Set<String> ignoreSuffixUris) {
        this.ignoreSuffixUris.addAll(ignoreSuffixUris);
    }

    @Override
    protected boolean shouldLog(HttpServletRequest request) {
        boolean infoEnabled = logger.isInfoEnabled();
        if (CollectionUtils.isEmpty(ignoreSuffixUris) || !infoEnabled) {
            return infoEnabled;
        }
        String requestURI = request.getRequestURI();
        return ignoreSuffixUris.stream().anyMatch(item -> !requestURI.endsWith(item));
    }

    /**
     * 不输出请求前的日志，只记录请求前的时间
     */
    @Override
    protected void beforeRequest(HttpServletRequest request, String message) {
        request.setAttribute(REQUEST_START_TIME_ATTR, System.currentTimeMillis());
    }

    @Override
    protected String createMessage(HttpServletRequest request, String prefix, String suffix) {
        StringBuilder msg = new StringBuilder();
        msg.append(prefix);
        msg.append("uri=").append(request.getRequestURI()).append(';');
        msg.append("method=").append(request.getMethod()).append(';');

        if (isIncludeQueryString()) {
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (Objects.nonNull(parameterMap)) {
                msg.append("parameterMap=").append(JsonUtils.serialize(parameterMap)).append(";");
            }
        }

        if (isIncludeHeaders()) {
            HttpHeaders headers = new ServletServerHttpRequest(request).getHeaders();
            Predicate<String> headerPredicate = getHeaderPredicate();
            msg.append("headers=");
            if (Objects.nonNull(headerPredicate)) {
                Map<String, String> h = new HashMap<>();
                Enumeration<String> names = request.getHeaderNames();
                while (names.hasMoreElements()) {
                    String headerName = names.nextElement();
                    if (headerPredicate.test(headerName)) {
                        h.put(headerName, headers.getFirst(headerName));
                    }
                }
                msg.append(JsonUtils.serialize(h));
            } else {
                msg.append(JsonUtils.serialize(headers));
            }
            msg.append(";");
        }

        if (isIncludePayload()) {
            String payload = getMessagePayload(request);
            if (payload != null) {
                msg.append("payload=").append(payload).append(";");
            }
        }
        Object value = request.getAttribute(REQUEST_START_TIME_ATTR);
        if (Objects.nonNull(value) && value instanceof Long startTime) { // 计算耗时,单位: 毫秒
            msg.append("time=").append(System.currentTimeMillis() - startTime).append("ms;");
        }
        msg.append(suffix);
        return msg.toString();
    }

    @Override
    protected void afterRequest(HttpServletRequest request, String message) {
        logger.info(message);
    }
}
